home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------------
- *
- * Macintosh Developer Technical Support
- *
- * Sample Control Panel Device and INIT Combination
- *
- * Program: INIT - CDEV
- * File: INIT.c - C Source
- *
- * Copyright © 1990 Apple Computer, Inc.
- * All rights reserved.
- *
- *-----------------------------------------------------------------------------*/
-
- #include <Common.h>
- #include <SAGlobals.h>
- #include <Processes.h>
-
-
- /*------------------------------------------------------------------------------
- Global variables
- ------------------------------------------------------------------------------*/
-
- CommonGlobalsRec gCommonGlobals; /* Globals common to INIT and CDEV */
- SessionRecord gSessionRecord; /* Data for PPC communications */
- long gTimeLastBeeped; /* Tickcount when we last beeped */
-
-
- /*------------------------------------------------------------------------------
- Procedure Prototypes and External Routines
- ------------------------------------------------------------------------------*/
-
- OSErr c_Install(void);
- OSErr DoPPCInit(void);
- OSErr ReadPreferences(void);
- void c_SystemTask(void);
- OSErr OpenAPort(void);
-
- pascal void InformCompProc(PPCParamBlockPtr ppb);
- pascal void WriteCompProc(PPCParamBlockPtr ppb);
- pascal void EndCompProc(PPCParamBlockPtr ppb);
-
- extern void a_SetA5Ref(A5RefType);
- extern A5RefType a_GetA5Ref(void);
-
-
- /*------------------------------------------------------------------------------
-
- OSErr c_Install(void)
-
- This is the C portion of the installation process. It is called from the
- assembly installation code at INIT time.
-
- We're going to be making all of our memory allocation from the System
- zone. There's really no other choice. We're an INIT; we don't have our own
- zone to get memory from. So we save off whatever the current zone is
- (which very well may be the System Zone -- what do I know?), and set the
- zone to the System Zone.
-
- Next, we get the memory for our globals. We call MakeA5World to do this,
- which is a modification of the routine described in Technote #256. If we
- don't get our memory, restore the zone and return to our caller with an
- "out of memory" error. If we do get the memory, then save off the old A5
- and set A5 to be our A5. Also, save our A5 reference so that when our
- SystemTask patch runs, it knows what value to use for A5.
-
- After that, we do some more initialization specific to the function of
- this INIT (which is to beep, annoying the **** out of the user). We call
- two subroutines for this, which do PPCToolbox setup, and read our
- preferences file.
-
- Finally, we clean up and leave. If any errors occured, we gett rid of the
- buffer for our globals. We then restore A5 and the zone, and return any
- error numbers.
-
- ------------------------------------------------------------------------------*/
-
- OSErr c_Install(void)
- {
- OSErr err; /* For any routines that return errors */
- THz oldZone; /* Holds the current zone while we switch
- over to the System zone for our memory
- allocation. */
- A5RefType A5Ref; /* The reference to our globals. This is
- created by the SAGlobals package. */
- long oldA5; /* Holds the current A5 when we switch
- over to the A5 for our own globals. */
-
- err = noErr;
- oldZone = GetZone();
- SetZone(SystemZone());
-
- MakeA5World(&A5Ref);
- if (A5Ref == nil) {
- SetZone(oldZone);
- return(memFullErr);
- }
- a_SetA5Ref(A5Ref);
- oldA5 = SetA5World(A5Ref);
-
- (void) DoPPCInit();
- (void) ReadPreferences();
-
- (void) SetA5(oldA5);
- SetZone(oldZone);
- return (err);
- }
-
-
- /*------------------------------------------------------------------------------
-
- OSErr DoPPCInit(void)
-
- Check to see if we have PPCToolbox facilities. If so, initialize the
- PPCToolbox, open a port, and perform a PPCInform call on the port (this is
- done with the EndCompProc procedure down below).
-
- If there are any errors, we set "err" to the result code. However, we have
- to distinguish between hard and soft errors. If the PPCToolbox is not
- around, that is a soft error, so we make sure to clear "err" if Gestalt
- says PPCToolbox is not there. Likewise, if the PPCInform call reports a
- result of "1", that means the asynchronous call is pending, and is also a
- soft error, so we also clear "err" in that case. Otherwise, any errors are
- hard errors, and indicate some serious problems. In that case, we report
- the error to our caller so that it can abort the installation of the INIT.
-
- ------------------------------------------------------------------------------*/
-
- OSErr DoPPCInit(void)
- {
- OSErr err; /* For any routines that return errors */
- long gestaltResult; /* Gestalt result. duh! */
-
- err = Gestalt(gestaltPPCToolboxAttr, &gestaltResult);
- if (!err) {
- err = PPCInit();
- if (!err) {
- err = OpenAPort();
- if (!err) {
- EndCompProc(&gSessionRecord.pb);
- err = gSessionRecord.pb.informParam.ioResult;
- if (err > 0) {
- err = noErr; /* cover up for soft errors */
- }
- }
- }
- } else
- err = noErr; /* cover up for soft errors */
-
- return (err);
- }
-
-
- /*------------------------------------------------------------------------------
-
- OSErr ReadPreferences(void)
-
- See if we can read the preferences file. First, we see if the FindFolder
- routine is present with a call to Gestalt. If so, we look for the
- preferences file. If one exists, we open it up, and read the values into
- our public globals buffer (the preferences file is just an image of that
- buffer, so the values read right in).
-
- If the FindFolder routine doesn't exist on this machine, or the
- preferences file doesn't exists, or there was an error when reading the
- file, we go right ahead and use a set of hard-wired values. All errors at
- this stage of the INIT are considered soft errors, so we clear "err".
-
- Finally, we set "gTimeLastBeeped" to be the current tickcount.
-
- ------------------------------------------------------------------------------*/
-
- OSErr ReadPreferences(void)
- {
- OSErr err; /* For any routines that return errors */
- long gestaltResult; /* Gestalt result. duh! */
- Boolean useDefaults; /* Boolean that we use to determine if we
- were able to read the preferences file.
- If not, this is TRUE, and we use default
- values instead. */
- FSSpec spec; /* FSSpec for reading our preferences */
- short refNum; /* Refnum for our preferences file */
- long amountToRead; /* Number of bytes to read from pref. */
-
- useDefaults = true;
- err = Gestalt(gestaltFindFolderAttr, &gestaltResult);
- if ((err == noErr) && (gestaltResult != 0)) {
- err = FindFolder(kOnSystemDisk, kPreferencesFolderType,
- kCreateFolder, &spec.vRefNum, &spec.parID);
- if (!err) {
- (void) PLstrcpy(spec.name, kPrefsFileName);
- err = FSpOpenDF(&spec, fsRdPerm, &refNum);
- if (!err) {
- amountToRead = sizeof(gCommonGlobals);
- err = FSRead(refNum, &amountToRead, (Ptr) &gCommonGlobals);
- if (!err) {
- err = FSClose(refNum);
- useDefaults = false;
- }
- }
- }
- }
-
- err = noErr;
-
- if (useDefaults) {
- gCommonGlobals.timesToBeep = 3;
- gCommonGlobals.beepInterval = 60*60; /* start off once a min. */
- }
- gTimeLastBeeped = TickCount();
-
- return(err);
- }
-
-
- /*------------------------------------------------------------------------------
-
- void c_SystemTask(void)
-
- This routine is the meat of the SystemTask() patch. It is called from
- a_SystemTask, an assembly routine that saves the registers, calls us,
- restores the registers, and then calls the real SystemTask() in a way that
- doesn't constitute a tail patch.
-
- All we do here is see if an appropriate amount of time has passed. If so,
- we beep a certain number of times. Otherwise, we do nothing.
-
- ------------------------------------------------------------------------------*/
-
- void c_SystemTask(void)
- {
- long oldA5;
- short loopy;
-
- oldA5 = SetA5World(a_GetA5Ref());
-
- if (TickCount() >= gTimeLastBeeped + gCommonGlobals.beepInterval) {
- for (loopy = 0; loopy < gCommonGlobals.timesToBeep; ++loopy) {
- SysBeep(5);
- }
- gTimeLastBeeped = TickCount();
- }
-
- (void) SetA5(oldA5);
- }
-
-
- /*------------------------------------------------------------------------------
-
- OSErr OpenAPort(void)
-
- Used to open a PPC port. We identify ourselves using the portName record.
- First, we give ourselves a name that will show up in the PPCBrowser
- (portName.name). We then give ourselves an identify to other processes. We
- can do this either by name or by creator/type signatures. In our case, we
- choose creator/type ('INCD'/'INIT').
-
- We then set up for a PPCOpen call. We are making a synchronous call, so we
- don't need a completion routine. ServiceType and resFlag are set to
- required values (per Inside Mac). We make the INIT not visible over the
- network. However, if we did, it's possible to talk to it from another
- Macintosh. If the other Mac know our communication protocol, it could
- change our beep parameters out from under us. Next, we point to the name
- record we want to use for our port, and use the default location name
- (which identifies our computer to other computers on the network).
-
- Finally, we make the PPCOpen call synchronously, and return any errors.
-
- ------------------------------------------------------------------------------*/
-
- OSErr OpenAPort(void)
- {
- gSessionRecord.portName.nameScript = GetScriptManagerVariable(smSysScript);
- (void) PLstrcpy(gSessionRecord.portName.name, "\pBG Beeper");
- gSessionRecord.portName.portKindSelector = ppcByCreatorAndType;
- #if GENERATING68K
- // Universal Interfaces 2.0
- gSessionRecord.portName.u.port.portCreator = kCreator;
- gSessionRecord.portName.u.port.portType = 'INIT';
- #else
- gSessionRecord.portName.u.port.creator = kCreator;
- gSessionRecord.portName.u.port.type = 'INIT';
- #endif
-
- gSessionRecord.pb.openParam.ioCompletion = nil;
- gSessionRecord.pb.openParam.serviceType = ppcServiceRealTime;
- gSessionRecord.pb.openParam.resFlag = 0;
- gSessionRecord.pb.openParam.networkVisible = false;
- gSessionRecord.pb.openParam.portName = &gSessionRecord.portName;
- gSessionRecord.pb.openParam.locationName = nil; // use the default location
-
- return(PPCOpen(&gSessionRecord.pb.openParam, false));
- }
-
-
- /*------------------------------------------------------------------------------
-
- pascal void InformCompProc(PPCParamBlockPtr ppb)
-
- After we open a PPC port, we make an asynchronous PPCInform call on it.
- When someone wants to talk to us, the completion routine for the PPCInform
- call gets called. That's this routine. When we get called, it means the
- CDEV wants to talk to us. We find out what it's asking by looking at the
- "userData" field, in which the CDEV has placed a message number. We only
- know kGetCommonGlobalsPtr, so we respond to that by returning the pointer
- to our public globals. After filling out the parameter block and i/o
- buffer, we make a PPCWrite call. We are called at interrupt time (we are a
- completion routine, remember), so we make the PPCWrite call
- asynchronously. We provide a completion routine that gets executed when
- the call completes.
-
- Because this is a completion routine, it is declared as using the Pascal
- calling convention.
-
- ------------------------------------------------------------------------------*/
-
- pascal void InformCompProc(PPCParamBlockPtr ppb)
- {
- OSErr err;
- GetCommonGlobalsPtr myBuffer;
-
- myBuffer = (GetCommonGlobalsPtr) ((SessionPtr)ppb)->buffer;
-
- ppb->writeParam.ioCompletion = (PPCCompProcPtr) WriteCompProc;
- ppb->writeParam.bufferLength = 0;
- ppb->writeParam.bufferPtr = (Ptr) myBuffer;
- ppb->writeParam.more = false;
-
- switch (ppb->informParam.userData) {
- case kGetCommonGlobalsPtr:
- ppb->writeParam.bufferLength = sizeof(GetCommonGlobalsRecord);
- myBuffer->commonGlobalsAddress = &gCommonGlobals;
- break;
- }
-
- err = PPCWriteAsync(&ppb->writeParam);
- }
-
-
- /*------------------------------------------------------------------------------
-
- pascal void WriteCompProc(PPCParamBlockPtr ppb)
-
- When our PPCWrite routine completes, the PPCToolbox calls this completion
- routine. All this is responsible for it closing the session on this end by
- calling PPCEnd.
-
- Because this is a completion routine, it is declared as using the Pascal
- calling convention.
-
- ------------------------------------------------------------------------------*/
-
- pascal void WriteCompProc(PPCParamBlockPtr ppb)
- {
- OSErr err;
-
- ppb->endParam.ioCompletion = (PPCCompProcPtr) EndCompProc;
-
- err = PPCEndAsync(&ppb->endParam);
- }
-
-
- /*------------------------------------------------------------------------------
-
- pascal void EndCompProc(PPCParamBlockPtr ppb)
-
- After the PPCEnd call completes, the PPCToolbox calls this completion
- routine. This routine is responsible for setting us up for receiving more
- PPC communications requestions. We also call this routine when we want to
- set ourselves for the very first PPCInform call.
-
- Because this is a completion routine, it is declared as using the Pascal
- calling convention.
-
- ------------------------------------------------------------------------------*/
-
- pascal void EndCompProc(PPCParamBlockPtr ppb)
- {
- OSErr err;
-
- ppb->informParam.ioCompletion = (PPCCompProcPtr) InformCompProc;
- ppb->informParam.autoAccept = true;
- ppb->informParam.portName = &((SessionPtr)ppb)->portName;
- ppb->informParam.locationName = &((SessionPtr)ppb)->locationName;
- ppb->informParam.userName = &((SessionPtr)ppb)->userName;
-
- err = PPCInformAsync(&ppb->informParam);
- }
-
-